import {isEmpty} from '@/utils/index.js';
import {createEdge, createEmptyNode, createGateway, createGatewayPair} from '@/components/flow/typs/flow.js';

export default class FlowModelInstance {
  model;
  edges;
  gatewayPairs;
  nodes;

  constructor(model) {
    this.model = model;
    this.edges = model.edges;
    this.gatewayPairs = model.gatewayPairs;
    this.nodes = model.nodes;
  }

  getAfterEdgesByNodeId(nodeId) {
    return (this.edges || []).filter(e => e.source === nodeId);
  }

  getBeforeEdgesByNodeId(nodeId) {
    return (this.edges || []).filter(e => e.target === nodeId);
  }

  addEdge(e) {
    this.edges.push(e);
  }

  addNode(node) {
    this.nodes.push(node);
  }

  getModel() {
    return this.model;
  }

  getNodeById(nodeId) {
    return this.nodes.find(n => n.id === nodeId) || undefined;
  }

  getNextNodesById(nodeId) {
    return this.getAfterEdgesByNodeId(nodeId).map(e => this.getNodeById(e.target)).filter(n => !!n);
  }

  getPreNodesById(nodeId) {
    return this.getBeforeEdgesByNodeId(nodeId).map(e => this.getNodeById(e.source)).filter(n => !!n);
  }

  removeNodeByIdAndTrim(nodeId) {
    this.removeNodeById(nodeId);
    this.trimModel();
  }

  removeNodeById(nodeId) {
    let nextNodes = this.getNextNodesById(nodeId);
    this.getModel().nodes = this.nodes = (this.nodes || []).filter(n => n.id !== nodeId);
    this.getModel().edges = this.edges = (this.edges || []).filter(n => n.source !== nodeId);
    this.getModel().gatewayPairs = this.gatewayPairs = (this.gatewayPairs || []).filter(g => g.source !== nodeId && g.target !== nodeId);

    if (!isEmpty(nextNodes)) {
      this.edges.forEach(e => {
        if (e.target === nodeId) {
          e.target = nextNodes[0].id;
        }
      });
    }
  }

  trimModel() {
    let flag = true;
    while (flag) {
      flag = false;
      //检查从开始网关直接指向结束网关的线
      let size = this.edges.length;
      this.model.edges = this.edges = this.edges.filter(e => !this.gatewayPairs.find(g => g.source === e.source && g.target === e.target));
      flag |= (this.edges.size < size);

      //检查从开始网关后面断开了，结束网关前面断开了，那就手动连接一条线
      this.gatewayPairs.forEach(gp => {
        if (this.getNextNodesById(gp.source).length <= 0 && this.getPreNodesById(gp.target).length <= 0) {
          this.addEdge(createEdge(gp.source, gp.target));
        }
      });

      //检查入度和出度小于等于1的网关
      let tempList = [...this.getNodes()];
      tempList.forEach(n => {
        if (n.type === 'gateway' && this.getNextNodesById(n.id).length <= 1 && this.getPreNodesById(n.id).length <= 1) {
          this.removeNodeById(n.id);
          flag = true;
        }
      });
    }
  }

  addNodeBefore(node, nodeId) {
    let edges = this.getBeforeEdgesByNodeId(nodeId);
    edges.forEach(e => e.target = node.id);
    this.addEdge(createEdge(node.id, nodeId));
    this.addNode(node);
  }

  addNodeAfter(node, nodeId) {
    let edges = this.getAfterEdgesByNodeId(nodeId);
    edges.forEach(e => e.source = node.id);
    this.addEdge(createEdge(nodeId, node.id));
    this.addNode(node);
  }

  addBranchForGateway(node, nodeId) {
    let gp = this.gatewayPairs.find(g => g.source === nodeId || g.target === nodeId);
    if (gp) {
      let endGateWayId = gp.target, startGateWayId = gp.source;
      this.addNode(node);
      this.addEdge(createEdge(startGateWayId, node.id));
      this.addEdge(createEdge(node.id, endGateWayId));
    }
  }

  addBranch(node, nodeId) {
    let befores = this.getPreNodesById(nodeId);
    let afters = this.getNextNodesById(nodeId);
    if (befores.length === 1 && afters.length === 1
      && this.gatewayPairs.find(g => g.source === befores[0].id && g.target === afters[0].id)) {
      this.addNode(node);

      this.addEdge(createEdge(befores[0].id, node.id));
      this.addEdge(createEdge(node.id, afters[0].id));
      return;
    }

    let startGateway = createGateway(),
      endGateway = createGateway();
    endGateway.executeWay = undefined;
    this.addNodeBefore(startGateway, nodeId);
    this.addNodeAfter(endGateway, nodeId);
    this.addNode(node);

    this.addEdge(createEdge(startGateway.id, node.id));
    this.addEdge(createEdge(node.id, endGateway.id));

    this.addGatewayPair(createGatewayPair(startGateway, endGateway));
  }

  addGatewayPair(g) {
    this.gatewayPairs.push(g);
  }

  getNodes() {
    return this.nodes || [];
  }

  isStart(nodeId) {
    return nodeId === 'start';
  }

  isEnd(nodeId) {
    return nodeId === 'end';
  }

  isStartGateWay(nodeId) {
    return !!(this.gatewayPairs || []).find(g => g.source === nodeId);
  }

  isEndGateWay(nodeId) {
    return !!(this.gatewayPairs || []).find(g => g.target === nodeId);
  }

  isUserTask(nodeId) {
    let node = this.nodes.find(n => n.id === nodeId);
    return node && node.type === 'userTask';
  }

  isGateWay(nodeId) {
    let node = this.nodes.find(n => n.id === nodeId);
    return node && node.type === 'gateway';
  }

  getModelString() {
    return JSON.stringify(this.getModel());
  }

  replaceNode(node) {
    let index = this.nodes.findIndex(n => n.id === node.id);
    if (index !== -1) {
      this.nodes[index] = node;
    }
  }

  getEdgeById(edgeId) {
    return (this.edges || []).find(e => e.id === edgeId);
  }

  addNewBranch(nodeId) {
    let node = createEmptyNode();
    let pres = this.getPreNodesById(nodeId) || [];
    let nexts = this.getNextNodesById(nodeId) || [];
    this.addNode(node);
    if (pres.length === 1
      && nexts.length === 1
      && !!this.gatewayPairs.find(g => g.source === pres[0].id && g.target === nexts[0].id)) {
      this.addEdge(createEdge(pres[0].id, node.id));
      this.addEdge(createEdge(node.id, nexts[0].id));
    } else {
      let startGateway = createGateway(), endGateway = createGateway();
      this.edges.forEach(e => {
        if (e.source === nodeId) {
          e.source = endGateway.id;
        }

        if (e.target === nodeId) {
          e.target = startGateway.id;
        }
      });

      this.addNode(startGateway);
      this.addNode(endGateway);
      this.addGatewayPair(createGatewayPair(startGateway, endGateway));

      this.addEdge(createEdge(startGateway.id, nodeId));
      this.addEdge(createEdge(nodeId, endGateway.id));

      this.addEdge(createEdge(startGateway.id, node.id));
      this.addEdge(createEdge(node.id, endGateway.id));
    }
  }

  addNewBranchForGateway(nodeId) {
    let node = createEmptyNode();
    this.addNode(node);
    this.gatewayPairs.forEach(gp => {
      if (gp.source === nodeId || gp.target === nodeId) {
        this.addEdge(createEdge(gp.source, node.id));
        this.addEdge(createEdge(node.id, gp.target));
      }
    });
  }

  getStartNode() {
    return this.nodes.find(n => n.id === 'start');
  }

  getEndNode() {
    return this.nodes.find(n => n.id === 'end');
  }
}